# -*- coding: utf-8 -*-

'''
    Created on 2009-11-22

    @author: Krzysztof Ociepa
'''

class FuzzyNeural(object):
    u"""Klasa zawiera realizacje systemu rozmyto-neuronowego.
    Posluzy on do realizacji estymacji czasow obslugi przychodzacych zadan
    """
    
    def __init__(self, k):
        '''Konstruktor (k - ilosc obslugiwanych klas)'''
        self.pA=[]
        self.pB=[]
        self.Rk=[]
        for i in range(k):
            li=[]
            for ii in range(100):
                li.append(1.0)
            self.Rk.append(li)
            self.pA.append([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0])
            self.pB.append([0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5])
        self.ny=1.0
        self.nua=1.0
        self.nub=1.0
        self.uR=[]
        self.yR=[]
        self.A=[]
        self.B=[]
        
    def set_param(self, A, B, pA, pB, Rk, nua, nub, ny, uR, yR):
        '''Ustawienie parametrow sieci: A, B, pA, pB, Rk, nua, nub, ny, uR, yR'''
        self.A=A
        self.B=B
        self.pA=pA
        self.pB=pB
        self.Rk=Rk    
        self.nua=nua
        self.nub=nub
        self.ny=ny
        self.uR=uR
        self.yR=yR
        
    def run(self, a, b, k):
        '''Odpowiedz sieci na sygnaly a i b dla klasy k=(0,1,2,...)-dla klasy zdefiniowano czasy w liscie Rk'''
        self.A=[]
        self.B=[]
        #przynaleznosc do pierwszego zbioru
        if a >= self.pA[k][0]:
            self.A.append(0)
        else:
            self.A.append((self.pA[k][0]-a)/self.pA[k][0])
        if b >= self.pB[k][0]:
            self.B.append(0)
        else:
            self.B.append((self.pB[k][0]-b)/self.pB[k][0])
        #przynaleznosc do zbioru 2
        if a == 0 or a >= self.pA[k][1]:
            self.A.append(0)
        else:
            if a <= self.pA[k][0]:
                self.A.append(a/self.pA[k][0])
            else:
                self.A.append((a-self.pA[k][1])/(self.pA[k][0]-self.pA[k][1]))    
        if b == 0 or b >= self.pB[k][1]:
            self.B.append(0)
        else:
            if b <= self.pB[k][0]:
                self.B.append(b/self.pB[k][0])
            else:
                self.B.append((b-self.pB[k][1])/(self.pB[k][0]-self.pB[k][1])) 
                
        #przynaleznosc do zbioru 3-9
        for i in (2, 3, 4, 5, 6, 7, 8):        
            if a <= self.pA[k][i-2] or a >= self.pA[k][i]:
                self.A.append(0)
            else:
                if a <= self.pA[k][i-1]:
                    self.A.append((a-self.pA[k][i-2])/(self.pA[k][i-1]-self.pA[k][i-2]))
                else:
                    self.A.append((a-self.pA[k][i])/(self.pA[k][i-1]-self.pA[k][i]))    
            if b <= self.pB[k][i-2] or b >= self.pB[k][i]:
                self.B.append(0)
            else:
                if b <= self.pB[k][i-1]:
                    self.B.append((b-self.pB[k][i-2])/(self.pB[k][i-1]-self.pB[k][i-2]))
                else:
                    self.B.append((b-self.pB[k][i])/(self.pB[k][i-1]-self.pB[k][i]))
         
        #przynaleznosc do 4 zbioru
        if a <= self.pA[k][7]:
            self.A.append(0)
        else:
            if a < self.pA[k][8]:
                self.A.append((a-self.pA[k][7])/(self.pA[k][8]-self.pA[k][7]))    
            else:
                self.A.append(1)
        if b <= self.pB[k][7]:
            self.B.append(0)
        else:
            if b < self.pB[k][8]:
                self.B.append((b-self.pB[k][7])/(self.pB[k][8]-self.pB[k][7]))    
            else:
                self.B.append(1)
                
        #obliczenie stopnia spelnienia przeslanki Rj
        self.uR=[]
        LM=range(10)
        for l in LM:
            for m in LM:
                self.uR.append(self.A[l]*self.B[m])
                
        #sprawdzenie regul
        self.yR=[]
        j=0
        for l in LM:
            for m in LM:
                if self.A[l]>0 and self.B[m]>0:
                    self.yR.append(self.Rk[k][j])
                else:
                    self.yR.append(0)
                ++j
                
        #wyostrzanie
        t=0
        for j in range(100):
            t+=self.yR[j]*self.uR[j]
         
        return t
    
    def get_error(self, tn, tr):
        '''Funkcja oblicza blad sredniokwadratowy dla wyjscia sieci (tn) oraz zmierzonego (tr)'''
        return 0.5*(tn-tr)*(tn-tr)
    
    def learn(self, a, b, k, tr):
        '''Funkcja odpowiada za adaptacje (uczenie) sieci'''
        #wyliczenie predykowanego czasu
        tn=self.run(a, b, k)
        #wyliczenie bledu
        e=self.get_error(tn, tr)
        #korekta wspolczynnikow wyjscia
        J=range(100)
        for j in J:
            self.Rk[k][j]+=self.ny*(tr-tn)*self.uR[j]
        #korekta wspolczynnikow wejscia 
        #LM=range(3)
        #for l in LM:
        #    self.pA[k][l]
        
        #zwrocic blad po 'nauce'
        return tr-self.run(a, b, k)
    
        
if __name__ == '__main__':
    print "SOA Fuzzy-Neural\nAutor: Krzysztof Ociepa\n"
    print "Functions:"
    print "FuzzyNeural(k) - Konstruktor, k: ilosc rozroznianych klas"
    print "set_param(A, B, pA, pB, Rk, nua, nub, ny, uR, yR) - ustawienie paramterow sieci"
    print "run(a, b, k) - odpowiedz sieci na wejscie a i b dla klasy k=(0,1,2,...)"
    print "get_error(tn, tr) - Blad sieci, tn-odp sieci, tr - wzorcowa odp"
    print "learn(a, b, k, tr) - Adaptacja sieci, wejscia a i b, klasa k=(0,1,...), tr-wzorcowa odp\n"
    